﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text;
using AliyunOpenSearch4Net.Base;
using AliyunOpenSearch4Net.App;
using AliyunOpenSearch4Net.Data;
using AliyunOpenSearch4Net.Error;
using AliyunOpenSearch4Net.Index;
using AliyunOpenSearch4Net.Search;
using AliyunOpenSearch4Net.Suggest;
using System.IO;

namespace AliyunOpenSearch4Net
{
    public partial class AliyunClient
    {
        private WebRequest CreateWebRequest(RequestBase request, string path, string method)
        {
            var body = string.Empty;

            var httpRequest = CreatePreparedWebRequest(request, path, method, out body);

            if (httpRequest.Method == HttpMethod.POST) // if the method is POST, write request body
            {
                httpRequest.ContentType = "application/x-www-form-urlencoded";

                using (var stream = httpRequest.GetRequestStream())
                {
                    WriteRequestBody(stream, body);
                }
            }

            return httpRequest;
        }



        internal TResponse GetResponse<TRequest, TResponse>(TRequest request, string path, string method)
            where TRequest : RequestBase<TResponse>
            where TResponse : Response, new()
        {
            var httpRequest = CreateWebRequest(request, path, method);
            var response = httpRequest.GetResponse();

            using (var stream = response.GetResponseStream())
            using (var reader = new StreamReader(stream, Encoding.UTF8, false))
            {
                var data = reader.ReadToEnd();
                return DeserializeObject<TResponse>(data);
            };
        }

        #region app operation

        /// <summary>
        /// Gets the application status.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <returns></returns>
        public Response<AppStatusResult> GetAppStatus(string appName)
        {
            var request = new AppStatusRequest
            {
                AppName = appName
            };

            return GetResponse<AppStatusRequest, Response<AppStatusResult>>(request, "/index/" + appName, HttpMethod.GET);
        }

        /// <summary>
        /// Deletes the application.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <returns></returns>
        public Response<int> DeleteApp(string appName)
        {
            var request = new AppDeleteRequest
            {
                AppName = appName
            };

            return GetResponse<AppDeleteRequest, Response<int>>(request, "/index/" + appName, HttpMethod.POST);
        }


        /// <summary>
        /// Creates the application.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="template">The template.</param>
        /// <returns></returns>
        public Response<AppCreateResult> CreateApp(string appName, string template)
        {
            if (string.IsNullOrEmpty(appName))
                throw new ArgumentNullException("appName");

            if (string.IsNullOrEmpty(template))
                throw new ArgumentNullException("template");

            var request = new AppCreateRequest
            {
                AppName = appName,
                Template = template
            };

            return GetResponse<AppCreateRequest, Response<AppCreateResult>>(request, "/index/" + appName, HttpMethod.POST);
        }

        /// <summary>
        /// Lists the application.
        /// </summary>
        /// <param name="page">The page index.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <returns></returns>
        public AppListResponse ListApp(int page, int pageSize)
        {
            var request = new AppListRequest
            {
                Page = page,
                PageSize = pageSize
            };

            return GetResponse<AppListRequest, AppListResponse>(request, "/index", HttpMethod.GET);
        }

        #endregion

        #region data operation

        /// <summary>
        /// Uploads the data.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <param name="items">The data push items.</param>
        /// <returns></returns>
        public Response UploadData(string appName, string tableName, IList<DataItem> items)
        {
            var request = new DataPushRequest
            {
                TableName = tableName,
                Items = items
            };

            return GetResponse<DataPushRequest, Response>(request, "/index/doc/" + appName, HttpMethod.POST);
        }

        #endregion

        #region error

        /// <summary>
        /// Gets the errors.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="page">page index.</param>
        /// <param name="pageSize">Size of the page.</param>
        /// <param name="sort">The sort.</param>
        /// <returns></returns>
        public Response<ErrorListResult> GetErrors(string appName, int page, int pageSize, SortMode sort)
        {
            var request = new ErrorListRequest
            {
                Page = page,
                PageSize = pageSize,
                SortMode = sort
            };

            return GetResponse<ErrorListRequest, Response<ErrorListResult>>(request, "/index/error/" + appName, HttpMethod.GET);
        }

        #endregion

        #region suggestions

        /// <summary>
        /// Gets the suggestions.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="query">The query used for getting the suggestions.</param>
        /// <param name="suggestName">Name of the suggest.</param>
        /// <param name="hint">The hint, the number of the suggestions you want to get.</param>
        /// <returns></returns>
        public SuggestResponse GetSuggestions(string appName, string query, string suggestName, int hint)
        {
            var request = new SuggestRequest
            {
                IndexName = appName,
                Query = query,
                SuggestName = suggestName,
                Hint = hint
            };

            return GetResponse<SuggestRequest, SuggestResponse>(request, "/suggest", HttpMethod.GET);
        }

        #endregion

        #region index

        /// <summary>
        /// Rebuilds the index.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="tableName">Name of the table.</param>
        /// <param name="operate">The operate.</param>
        /// <returns></returns>
        public Response RebuildIndex(string appName, string tableName, string operate)
        {
            var request = new IndexRebuildRequest
            {
                TableName = tableName,
                Operate = operate
            };

            return GetResponse<IndexRebuildRequest, Response>(request, "/index/" + appName, HttpMethod.GET);
        }

        #endregion

        #region search

        /// <summary>
        /// Searches within the specified application name.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="query">The query used for searching.</param>
        /// <param name="fetchFields">The fetch fields.</param>
        /// <param name="disabled">if set to <c>true</c> [disabled].</param>
        /// <param name="firstFormulaName">First name of the formula.</param>
        /// <param name="formulaName">Name of the formula.</param>
        /// <param name="qp">The qp.</param>
        /// <param name="summary">The summary.</param>
        /// <returns></returns>
        public Response<SearchResult> Search(string appName, string query, string fetchFields = null, bool disabled = false, string firstFormulaName = null, string formulaName = null, string qp = null, SearchSummary summary = null)
        {
            var request = new SearchRequest
            {
                IndexName = appName,
                Query = query,
                FetchFields = fetchFields,
                Disable = disabled,
                FirstFormulaName = firstFormulaName,
                FormulaName = formulaName,
                QP = qp,
                Summary = summary
            };

            return GetResponse<SearchRequest, Response<SearchResult>>(request, "/search", HttpMethod.GET);
        }

        /// <summary>
        /// Scans the specified application name.
        /// </summary>
        /// <param name="appName">Name of the application.</param>
        /// <param name="query">The query.</param>
        /// <param name="scroll">The scroll.</param>
        /// <param name="searchType">Type of the search.</param>
        /// <param name="scrollId">The scroll id.</param>
        /// <returns></returns>
        public Response<ScanResult> Scan(string appName, string query, string scroll, string searchType = null, string scrollId = null)
        {
            var request = new ScanRequest
            {
                IndexName = appName,
                Query = query,
                Scroll = scroll,
                ScrollId = scrollId,
                SearchType = searchType
            };

            return GetResponse<ScanRequest, Response<ScanResult>>(request, "/search", HttpMethod.GET);
        }

        #endregion
    }
}
